/**
 * 
 */
package ar.uba.fi.tonyvaliente.tree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author santiago
 *
 */
public class LeafBPlusTreeNode<K extends Comparable<K>, V> extends BPlusTreeNode<K, V> {
	
	private static Logger log = Logger.getLogger(LeafBPlusTreeNode.class.getName());


	private Reference<K, V> previous;
	private Reference<K, V> next;
	private Map<K, V> datos;
	
	protected LeafBPlusTreeNode(Reference<K, V> parent) {
		super(parent);
		this.datos = new TreeMap<K, V>();
		this.cantidadElementos = 0;
		this.maxCantidadElementos = 0;
	}
	
	protected LeafBPlusTreeNode(Reference<K, V> parent, Integer maxCantidadElementos) {
		super(parent, maxCantidadElementos);
		this.datos = new TreeMap<K, V>();
		this.cantidadElementos = 0;
		this.maxCantidadElementos = maxCantidadElementos;
	}

	protected LeafBPlusTreeNode(Reference<K, V> parent, BPlusTree<K, V> tree,
			Integer maxCantidadElementos) {
		super(parent, tree, maxCantidadElementos);
		this.datos = new TreeMap<K, V>();
		this.cantidadElementos = 0;
		this.maxCantidadElementos = maxCantidadElementos;
	}
	
	protected boolean isInner() {
		return false;
	}

	protected boolean isLeaf() {
		return true;
	}

	/* (non-Javadoc)
	 * @see ar.uba.fi.datos.tonyvaliente.tree.BPlusTreeNode#add(java.lang.Comparable, java.lang.Object)
	 */
	@Override
	protected AddResult<K, V> add(K key, V value) throws BPlusTreeException {
		log.fine("añadiendo el par: " + key + "=" + value);
		System.out.println("añadiendo el par: " + key + "=" + value);
		AddResult<K, V> result = null;
		
		if(!datos.containsKey(key)){
			this.cantidadElementos++;
		}
		
		// Insertamos el registro y luego verificamos si hay que hacer split.
		this.datos.put(key, value);
		
		if(this.getCantidadElementos().compareTo(this.getTree().getElementosPorNodo()) > 0){
			result = this.split();
		}else{
			this.getThisReference().setNode(this);
			tree.getDirtyNodes().add(this.getThisReference());
		}
		
		return result;
	}

	private AddResult<K, V> split() throws BPlusTreeException {

		// Creamos los dos nodos que van a surgir del split,
		// sus referencias y sus contenidos.
//		Reference<K, V> leftReference = this.getTree().createReference();
//		LeafBPlusTreeNode<K, V> leftNode = new LeafBPlusTreeNode<K, V>(this.getParent(), this.getTree(), this.getMaxCantidadElementos());
//		Map<K,V> leftData = new TreeMap<K, V>();
//
//		leftReference.setNode(leftNode);
//		
//		leftNode.setDatos(leftData);
		
		Reference<K, V> leftReference = this.getThisReference();
		
		
		Reference<K, V> rightReference = this.getTree().createReference();
		LeafBPlusTreeNode<K, V> rightNode = new LeafBPlusTreeNode<K, V>(this.getParent(), this.getTree(), this.getMaxCantidadElementos());
		Map<K,V> rightData = new TreeMap<K, V>();

		rightReference.setNode(rightNode);
		
		rightNode.setDatos(rightData);
		
		// Tomamos el valor medio
		int medio = this.getCantidadElementos()/2;
		
		// Pasamos las claves a una lista ordenada
		List<K> claves = new ArrayList<K>(this.datos.keySet());
		Collections.sort(claves);
		
		// Obtenemos la clave media, esta será la clave a promover.
		K claveMedia = claves.get(medio);
		
		// Copiamos los valores a los nuevos nodos.
		for (K key : claves) {
			V value = this.datos.get(key);
//			if(key.compareTo(claveMedia) < 0){
//				leftData.put(key, value);
//			}else{
//				rightData.put(key, value);
//			}
			if(key.compareTo(claveMedia) >= 0){
				this.getDatos().remove(key);
				rightData.put(key, value);
			}
		}
		
//		leftNode.setPrevious(this.previous);
		rightNode.setNext(this.next);
		this.setNext(rightReference);
		rightNode.setPrevious(leftReference);
		
		this.setCantidadElementos(this.getDatos().size());
		rightNode.setCantidadElementos(rightData.size());
		
		
		tree.getDirtyNodes().add(leftReference);
		tree.getDirtyNodes().add(rightReference);
		
		AddResult<K, V> resultado = new AddResult<K, V>();
		resultado.setLeftReference(leftReference);
		resultado.setRightReference(rightReference);
		resultado.setKey(claveMedia);
		
		return resultado;
	}

	/* (non-Javadoc)
	 * @see ar.uba.fi.datos.tonyvaliente.tree.BPlusTreeNode#get(java.lang.Comparable)
	 */
	@Override
	protected V get(K key) {
		return this.getDatos().get(key);
	}

	public Map<K, V> getDatos() {
		return datos;
	}

	protected void setDatos(Map<K, V> datos) {
		this.datos = datos;
	}

	protected Reference<K, V> getPrevious() {
		return previous;
	}

	protected void setPrevious(Reference<K, V> previous) {
		this.previous = previous;
	}

	public Reference<K, V> getNext() {
		return next;
	}

	protected void setNext(Reference<K, V> next) {
		this.next = next;
	}

	@Override
	public String toString() {
		
		return this.datos.toString();
	}
}
